//
//  BigNumber.cpp
//  ATM Killer
//
//  Created by USoft on 6/19/14.
//
//

#include "BigNumber.h"
using namespace std;
//CONSTRUCTORS & DECONSTRUCTOR
BigNumber::BigNumber() {
    
}
BigNumber::~BigNumber() {
    delete[] this->number;
}
bool BigNumber::init()
{
    return true;
}

void BigNumber::setNumber(int *var)
{
    auto tempNumber = this->number;
    this->number = var;
    delete[] tempNumber;
}
int* BigNumber::getNumber()
{
    return this->number;
}
void BigNumber::initialize(int a) {
    this->number = new int[100];
    //check the number of character of a
    char aString[100];
    sprintf(aString, "%d",a);
    this->nDigits = (int)strlen(aString);
    //init
    for(int i = 0; i < 100; i++)
        this->number[i] = 0;
    for(int i = 1; i <= nDigits;i++)
    {
        int product = pow(10,i);
        this->number[i-1] = (a % product) / (product/10);
    }
}

void BigNumber::initialize(const char* a) {
    this->number = new int[100];
    for(int i = 0; i < 100; i++){
        this->number[i] = 0;
    }
    for(int i = (int)strlen(a) - 1;i >= 0;i--){
        std::string aString = a;
        char *aTest = new char[1];
        sprintf(aTest, "%c", aString.at(i));
        this->number[strlen(a) - 1 - i] = atoi(aTest);
        delete[] aTest;
    }
    this->nDigits = (int)strlen(a);
}

//PUBLIC FUNCTIONS
BigNumber* BigNumber::add(int temp)
{
    BigNumber* tempBig = BigNumber::create();
    tempBig->initialize(temp);
    BigNumber* finalResult = BigNumber::create();
    finalResult->initialize(0);
    finalResult->setNumber(this->addArray(this->number,tempBig->number,imax(this->nDigits,tempBig->nDigits)));
    finalResult->checkNDigits();
    return finalResult;

}
BigNumber* BigNumber::add(BigNumber *b) {
    BigNumber *finalResult = BigNumber::create();
    finalResult->initialize(0);
    finalResult->setNumber(this->addArray(this->number,b->number,imax(this->nDigits,b->nDigits)));
    finalResult->checkNDigits();
    return finalResult;
}

BigNumber* BigNumber::minus(BigNumber *b) {
    BigNumber *finalResult = BigNumber::create();
    finalResult->initialize(0);
    if(this->compare(b) == 1)
    {
        finalResult->setNumber(this->minusArray(this->number,b->number,imax(this->nDigits,b->nDigits)));
        finalResult->checkNDigits();
    }
    else
    {
        finalResult->nDigits = 1;
        finalResult->toZero();
    }
    return finalResult;
}

BigNumber* BigNumber::multiplyAnElement(int a) {
    BigNumber* finalResult = BigNumber::create();
    finalResult->initialize(0);
    finalResult->setNumber(this->multiplyArrayWithAnElement(this->number, a));
    finalResult->checkNDigits();
    return finalResult;
}


BigNumber* BigNumber::complexMultiply(BigNumber *b) {
    BigNumber *finalResult = BigNumber::create();
    finalResult->initialize(0);
    for(int i = 0; i < b->nDigits; i++)
    {
        BigNumber *temp = BigNumber::create();
        char* thisString = this->toString();
        temp->initialize(thisString);
        delete[] thisString;
        temp = temp->multiplyAnElement(b->number[i]);
        for(int j = 0; j < i; j++)
        {
            temp = temp->multiplyAnElement(10);
        }
        finalResult = finalResult->add(temp);
    }
    
    return finalResult;
}

BigNumber* BigNumber::multiplyRationalNumber(double a) {
    //init
    BigNumber* result = BigNumber::create();
    char* thisString = this->toString();
    result->initialize(thisString);
    delete[] thisString;
    //*100 this->number
    result = result->multiplyAnElement(10);
    result = result->multiplyAnElement(10);
    //*100 float
    double aTemp = a * 100;
    char* aTempString = new char[100];
    sprintf(aTempString, "%.0lf", aTemp);
    BigNumber *aTempBig = BigNumber::create();
    aTempBig->initialize(aTempString);
    delete[] aTempString;
    
    result= result->complexMultiply(aTempBig);
    BigNumber *finalResult = BigNumber::create();
    finalResult->initialize(0);
    for(int i = 4; i < result->nDigits; i++)
    {
        finalResult->number[i - 4] = result->number[i];
    }
    finalResult->nDigits = result->nDigits - 4;
    //final Result
    return finalResult;
}


BigNumber* BigNumber::power(float a, int b) {
    BigNumber *finalResult = BigNumber::create();
    finalResult->initialize(1);
    int i=0;
    while(pow(a, i) < pow(10, 7)){
        i++;
    }
    if(i>0){
        int j1 = (int)b / i;
        int remainder = b % i;
        BigNumber* temp= BigNumber::create();
        temp->initialize((int)pow(a,i));
        for (int k = 0; k < j1; k++){
            finalResult = finalResult->complexMultiply(temp);
        }
        BigNumber* temp1 = BigNumber::create();
        temp1->initialize(pow(a,remainder));
        finalResult = finalResult->complexMultiply(temp1);
    }
    return finalResult;
}

int BigNumber::compare(BigNumber *b) {
    if(this->nDigits > b->nDigits)
        return 1;
    else if(this->nDigits < b->nDigits)
        return -1;
    else{
        for(int i = this->nDigits - 1; i >= 0; i--){
            if(this->number[i] > b->number[i])
                return 1;
            else if (this->number[i] < b->number[i])
                return -1;
        }
        return 0;
    }
}

void BigNumber::print() {
    for(int i = 99; i >= 0; i--){
        printf("%d", this->number[i]);
    }
    printf("\n");
}

char* BigNumber::toString() {
    char *a = new char[100];
    for(int i = this->nDigits - 1; i >= 0; i--){
        sprintf(&a[this->nDigits - 1 - i], "%d", this->number[i]);
    }
    return a;
}

//char* BigNumber::display(){
//    char*a = this->toString();
//    string b;
//    for(int i = 0; i < strlen(a); i++){
//        if((strlen(a) - 1 - i) % 3 == 0 && (strlen(a) - 1 - i) != 0){
//            b += a[i];
//            b += '.';
//        }
//        else{
//            b += a[i];
//        }
//        
//    }
//    free(a);
//    char* result = new char[100];
//    copy(b.begin(), b.end(), result);
//    result[b.size()] = '\0';
//    return result;
//}
char* BigNumber::display(){
    char*a = this->toString();
    string b;
    if(strlen(a)<=6){
        for(int i = 0; i < strlen(a); i++){
            if((strlen(a) - 1 - i) % 3 == 0 && (strlen(a) - 1 - i) != 0){
                b += a[i];
                b += '.';
            }
            else{
                b += a[i];
            }
            
        }
    }
    else if(strlen(a)<=9){
        for(int i = 0; i < strlen(a)-6; i++){
            if((strlen(a) - 1 - i) % 3 == 0 && (strlen(a) - 7 - i) != 0){
                b += a[i];
                b += '.';
            }
            else{
                b += a[i];
            }
            
        }
        b += ",";
        b += a[strlen(a)-6];
        b += a[strlen(a)-5];
        b += " millions";
    }
    else{
        for(int i = 0; i < strlen(a)-9; i++){
            if((strlen(a) - 1 - i) % 3 == 0 && (strlen(a) - 10 - i) != 0){
                b += a[i];
                b += '.';
            }
            else{
                b += a[i];
            }
        }
        b += ",";
        b += a[strlen(a)-9];
        b += a[strlen(a)-8];
        b += " billions";
    }
    
    
    free(a);
    char* result = new char[100];
    copy(b.begin(), b.end(), result);
    result[b.size()] = '\0';
    return result;
}
//PRIVATE FUNCTIONS
int* BigNumber::addArray(int* a1,int *a2, int nDigits) {
    int remainder = 0;
    int *a3;
    a3 = new int[100];
    for(int i = 0; i < 100;i++)
        a3[i] = 0;
    for (int i = 0; i < nDigits + 1; i++)
    {
        a3[i] = (a1[i] + a2[i] + remainder) % 10;
        remainder = (a1[i] + a2[i] + remainder) / 10;
    }
    return a3;
}

int* BigNumber::minusArray(int* a1,int* a2, int nDigits) {
    int remainder = 0;
    int *a3;
    a3 = new int[100];
    for(int i = 0; i < 100; i++)
        a3[i] = 0;
    for(int i = 0; i < nDigits + 1; i++)
    {
        a3[i] = (a1[i] - a2[i] + remainder) % (10);
        if(a3[i] < 0) a3[i] += 10;
        if(a1[i] - a2[i] + remainder<0)
            remainder = -1;
        else remainder = 0;
    }
    return a3;
}
int* BigNumber::multiplyArrayWithAnElement(int*a1,int a) {
    if (a < 0 || a > 10)
        return 0;
    else
    {
        int remainder = 0;
        int *a3;
        a3 = new int[100];
        for(int i = 0; i < 100; i++)
            a3[i] = 0;
        for(int i = 0; i < this->nDigits + 1; i++)
        {
            a3[i] = (a1[i] * a + remainder) % 10;
            remainder = (a1[i] * a + remainder) / 10;
        }
        return a3;
    }
}

void BigNumber::checkNDigits() {
    int count = 0;
    for(int i = 0; i < 100; i++){
        if(this->number[i] != 0)
            count = i + 1;
    }
    this->nDigits = count;
}

void BigNumber::toZero() {
    for(int i = 0; i < 100; i++)
        this->number[i] = 0;
}
